import {
  AfterContentInit,
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  Component,
  ContentChild,
  ContentChildren,
  forwardRef,
  Input,
  OnChanges,
  OnDestroy,
  QueryList,
  SimpleChanges,
  ViewEncapsulation,
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { merge, Subject } from 'rxjs';
import { startWith, takeUntil } from 'rxjs/operators';
import { InputBoolean } from '../core/api';
import { CheckboxComponent } from './checkbox.component';
import { CheckboxAllComponent } from './checkbox-all.component';

@Component({
  selector: 'lv-checkbox-group, [lv-checkbox-group]',
  exportAs: 'lvCheckboxGroup',
  template: ' <ng-content></ng-content> ',
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef((): typeof CheckboxGroupComponent => CheckboxGroupComponent),
      multi: true,
    },
  ],
  host: {
    '[class.lv-checkbox-group]': 'true',
  },
})
export class CheckboxGroupComponent implements OnChanges, AfterContentInit, OnDestroy, ControlValueAccessor {
  @Input() @InputBoolean() lvDisabled;
  @ContentChild(CheckboxAllComponent, { static: false })
  checkboxall: CheckboxAllComponent;

  @ContentChildren(forwardRef((): typeof CheckboxComponent => CheckboxComponent), { descendants: true })
  checkboxs: QueryList<CheckboxComponent>;

  public checkAllStatus: 0 | 1 | 2; // 0 未选中 1 全选 2 部分选中
  private selected = [];
  private checkSubscribe;

  private destroy$ = new Subject<void>();

  constructor(private cdr: ChangeDetectorRef) {}

  registerOnChange(fn: any): void {
    this._onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this._onTouch = fn;
  }

  ngOnDestroy(): void {
    this.destroy$.next();
    this.destroy$.complete();
  }

  selectedChange(value): void {
    this.selected = value;
    this._onChange(this.selected);
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (changes.lvDisabled) {
      this.setDisableStatus();
    }
  }

  writeValue(value: any): void {
    this.selected = value;
    this.updateCheckboxs();
    this.setGroupCheckStatus();
    this.cdr.markForCheck();
  }

  isChecked(val): boolean {
    const _selected = !this.selected ? [] : Array.from(this.selected);
    return _selected.some((v): boolean => v === val);
  }

  updateCheckboxAll(checked, partialChecked): void {
    if (this.checkboxall) {
      this.checkboxall.checked = checked;
      this.checkboxall.lvIndeterminate = partialChecked;
      this.checkboxall.markForCheck();
    }
  }

  setDisableStatus(): void {
    if (this.checkboxs) {
      this.checkboxs.forEach((item): void => {
        if (item.lvDisabled === undefined) {
          item.disabled = this.lvDisabled;
        }
        item.markForCheck();
      });
    }
    if (this.checkboxall) {
      this.checkboxall.disabled = this.lvDisabled;
      this.checkboxall.markForCheck();
    }
  }

  updateCheckboxs(isManual?: boolean): void {
    // 外部手动触发时禁用状态的值不能修改，内部赋值时可修改
    if (this.checkboxs) {
      const _selected = !this.selected ? [] : Array.from(this.selected);
      this.checkboxs.forEach((item): void => {
        const canCheck =
          (this.checkboxall && !this.checkboxall.lvIgnoreDisabled) || !isManual || (isManual && !item.lvDisabled);
        if (canCheck) {
          item.checked = _selected.includes(item.lvValue) || false;
          item.markForCheck();
        }
      });
    }
  }

  ngAfterContentInit(): void {
    // tslint:disable-next-line:deprecation
    this.checkboxs.changes.pipe(startWith(null), takeUntil(this.destroy$)).subscribe((): void => {
      setTimeout((): void => {
        this.updateCheckboxs();
        this.setDisableStatus();
        this.subscribeChange();
        this.setGroupCheckStatus();
      }, 0);
    });

    if (this.checkboxall) {
      this.checkboxall.select$.pipe(takeUntil(this.destroy$)).subscribe((item: any): void => {
        this.checkStatusChange(item.checked);
      });
    }

    this.subscribeChange();
  }

  subscribeChange(): void {
    if (this.checkSubscribe) {
      this.checkSubscribe.unsubscribe();
    }
    this.checkSubscribe = merge(...this.checkboxs.map((item): Subject<any> => item.select$))
      .pipe(takeUntil(this.destroy$))
      .subscribe((item: any): void => {
        const _selected = !this.selected ? [] : Array.from(this.selected);
        const isChecked = _selected.includes(item.lvValue);
        const index = _selected.findIndex((value): boolean => value === item.lvValue);
        if (!isChecked) {
          _selected.push(item.lvValue);
        } else {
          _selected.splice(index, 1);
        }

        this.selectedChange(_selected);
        this.setGroupCheckStatus(true);
      });
  }

  checkStatusChange(checkAllStatus): void {
    if (!this.checkboxs) {
      return;
    }

    if (checkAllStatus === false) {
      const arr = this.getArrWhenNotCheckAllStatus();
      this.selectedChange(arr);
      this.updateCheckboxs(true);
    } else if (checkAllStatus === true) {
      const arr = this.getArrWhenCheckAllStatus();
      this.selectedChange(arr);
      this.updateCheckboxs(true);
    }

    const delay = setTimeout((): void => {
      this.setGroupCheckStatus(true);
      clearTimeout(delay);
    }, 0);
  }

  getArrWhenNotCheckAllStatus(): any {
    const arr = [];
    if (!this.checkboxall.lvIgnoreDisabled) {
      return [];
    }
    if (this.checkboxs) {
      this.checkboxs.forEach((item): void => {
        if (item.lvDisabled && item.checked) {
          arr.push(item.lvValue);
        }
      });
    }
    return arr;
  }

  getArrWhenCheckAllStatus(): any {
    const arr = [];
    if (this.checkboxs) {
      this.checkboxs.forEach((item): void => {
        if (!this.checkboxall.lvIgnoreDisabled) {
          arr.push(item.lvValue);
        }
        if (!item.lvDisabled || (item.lvDisabled && item.checked)) {
          arr.push(item.lvValue);
        }
      });
    }
    return arr;
  }

  // set group check status
  setGroupCheckStatus(isManual?: boolean): void {
    if (!this.checkboxall) {
      return;
    }

    let newSelected = [];
    let newCheckboxes = [];
    if (this.checkboxall.lvIgnoreDisabled) {
      if (this.checkboxs) {
        this.checkboxs.forEach((item): void => {
          if (item.lvDisabled) {
            return;
          }
          if (item.checked) {
            newSelected.push(item.lvValue);
          }
          newCheckboxes.push(item);
        });
      }
    } else {
      newSelected = this.selected;
      newCheckboxes = this.checkboxs as any;
    }

    if (newSelected.length === 0) {
      this.updateCheckboxAll(false, false);
      if (this.checkAllStatus !== 0) {
        this.checkAllStatus = 0;
        if (isManual) {
          this.checkboxall.lvCheckAllStatusChange.emit(0);
        }
      }
    } else if (newSelected.length === newCheckboxes.length) {
      this.updateCheckboxAll(true, false);
      if (this.checkAllStatus !== 1) {
        this.checkAllStatus = 1;
        if (isManual) {
          this.checkboxall.lvCheckAllStatusChange.emit(1);
        }
      }
    } else {
      this.updateCheckboxAll(false, true);
      if (this.checkAllStatus !== 2) {
        this.checkAllStatus = 2;
        if (isManual) {
          this.checkboxall.lvCheckAllStatusChange.emit(2);
        }
      }
    }
  }

  private _onTouch: any = (): void => {};

  private _onChange: any = (): void => {};
}
